from tree_sitter import Language, Parser, Tree, Node
import tree_sitter_cpp
import os, json
from .type_get import TypeGet
from .identifier_get import IdentifierGet

class GlobalAnalyze:
    def __init__(self, project_path):
        self.project_path = project_path
        self.file_path = None
        self.language = Language(tree_sitter_cpp.language())
        self.parser = Parser(self.language)
        self.tree = None
        self.type_get = TypeGet()
        self.identifier_get = IdentifierGet()
        self.defined_types: dict = {}

    def parse_file(self) -> None:
        with open(self.file_path, 'rb') as file:
            code = file.read()
            self.tree = self.parser.parse(code)

    def get_all_files(self) -> list[str]:
        all_files = []
        for root, _, files in os.walk(self.project_path):
            for file in files:
                all_files.append(os.path.join(root, file))
        return all_files
    
    def global_analyze(self) -> None:
        def traverse(node: Node):
            if node.type == "struct_specifier":
                self.ectract_struct_definitions(node)
            if node.type == "preproc_def":
                self.extract_macro_definitions(node)

            for child in node.children:
                traverse(child)

        all_files = self.get_all_files()
        for file in all_files:
            if file.endswith('.c') or file.endswith('.h'):
                self.file_path = file
                self.parse_file()
                if self.tree and self.tree.root_node:
                    traverse(self.tree.root_node)
        
        json.dump(self.defined_types, open("type_definitions.json", "w"), indent=4)   
    
    def ectract_struct_definitions(self, node: Node) -> None:
        struct_name = None
        is_definition = False
        for child in node.named_children:
            if child.type == "type_identifier":
                struct_name = child.text.decode("utf-8")
                continue
            if child.type == "field_declaration_list":
                is_definition = True
                continue
        if is_definition and struct_name:
            struct_start = node.start_point
            struct_end = node.end_point
            self.defined_types[struct_name] = {
                "name": struct_name,
                "file": self.file_path,
                "start": struct_start,
                "end": struct_end,
                "type": "struct",
            }
    
    def extract_macro_definitions(self, node: Node) -> None:
        macro_name = None
        macro_value = None
        for child in node.named_children:
            try:
                if child.type == "identifier":
                    macro_name = child.text.decode("utf-8")
                elif child.type == "preproc_arg":
                    macro_value = child.text.decode("utf-8")
            except:
                print(f"Error decoding macro definition: {child.type}, {child.text}, {self.file_path}, {node.start_point}")
        if macro_name:
            macro_start = node.start_point
            macro_end = node.end_point
            self.defined_types[macro_name] = {
                "name": macro_name,
                "value": macro_value,
                "file": self.file_path,
                "start": macro_start,
                "end": macro_end,
                "type": "macro",
            }